home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
public
/
bit
/
src
/
mpeg.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
21KB
|
890 lines
/*
* $Id: mpeg.c,v 0.91 1994/02/20 00:52:55 zhao Pre-Release $
*
*. This file is part of BIT shareware package. After the two weeks of
* free evaluation period, you are encouraged (required) to register
* your copy for a small registration fee, which is $35 for personal use
* and $50 for commercial, government and institutional use.
*
* Copyright(c) 1993, 1994 by T.C. Zhao.
* All rights reserved.
*
* Permission to use, copy, and distribute this software in its entirety
* for non-commercial purposes is hereby granted, provided that the
* above shareware and copyright notices and this permission notice
* appear in all copies and their documentation.
*
* This software may be modified for your own use, but modified versions
* may not be distributed without prior consent of the author.
*
* This software is provided "as is" without expressed or implied
* warranty of any kind.
*
*.
*
* Rudimentary MPEG support. Seeking is done via the brute force
* method, that is actually reading the frames one by one.
*
* Uses Berkeley's library with its good and evil ...
* No sound support.
*
* BUGS: seeking does not work correctly
*/
#ifndef NO_MPEG
#if !defined(lint) && defined(F_ID)
char *id_mpg = "$Id: mpeg.c,v 0.91 1994/02/20 00:52:55 zhao Pre-Release $";
#endif
#include "bit.h"
#include "extern.h"
#include "mpeg/video.h" /* for VidStream definaitons */
#include "mpeg/mlib.h" /* for prototypes */
/************* Defines and limits ************************/
#define SEEKSMALL 5
#define SEEKLARGE 10
/************ Local varialbes ****************************/
static int cur_frame_no; /* current frame number */
static int actual_frames; /* total no. frames */
static int magfactor = 2; /* default maginifcation */
static int current_fdelay; /* frame delay */
static int loop_back; /* true if play & loop */
static void *oldpic; /* old BIT image */
static VidStream *cur_frame; /* current video stream */
static FL_OBJECT *fn; /* frame number */
static IMG_IO *rgb_wio[30]; /* all RGB writing func */
static int currentwrite; /* current selected func */
static int total_rgbwrite;
/******************* Local functions ***************************/
static void update_frame_number(void);
static FL_FORM *create_form_mpg(void);
static void show_play_rate(float);
static void MPEG_rewind(IPTR);
static int MPEG_convert(int);
static void frame_to_bit(IPTR);
static void fake_frame_to_bit(IPTR);
static void frame_to_disk(void);
static void push_globals(IPTR); /* save zoom factors etc. */
static void pop_globals(IPTR); /* restore zoom factors etc */
static void MpegDisplay(VidStream * vid);
static void MPEG_play(IPTR, int);
/*********************************************************************
* Get dimensions and other information about MPEG. Initalize
* the decoder and other things. Due to the use of library, MPEG_desc
* may cause the 1st frame being loaded. MPEG_load will check that
********************************************************************/
int
MPEG_desc(IPTR im)
{
create_form_mpg();
set_video_stream(im->fp);
frame_re_init();
reset_mpeg_stream();
actual_frames = 400;
/* the first frame. Zero indicates initalization */
if (!(cur_frame = mpeg_next_frame(0)))
{
Bark("MPEG_Desc", "Something is wrong");
return -1;
}
im->w = cur_frame->mb_width * 16;
im->h = cur_frame->mb_height * 16;
/* tell the driver that we are dealing with multi-images */
im->more = 1;
cur_frame_no = 0;
update_frame_number();
return 0;
}
/*****************************************************************
* Load and show the first frame
******************************************************************/
int
MPEG_load(IPTR im)
{
/* if MPEG_desc did not load the 1st frame, load it now */
if ((cur_frame && cur_frame->current) ||
((cur_frame = mpeg_next_frame(1)) && cur_frame->current))
{
/* must pass im 'cause imgptr is updated AFTER xxx_load returns */
frame_to_bit(im);
}
cur_frame_no = 1;
update_frame_number();
return (cur_frame && cur_frame->current) ? 0 : -1;
}
/***********************************************************************
* After displaying the first frame, the driver will call this routine
* to load next frame. For mpeg, we take control and never return
* until the last frame or whatever the user wants to do with the
* video stream
***********************************************************************/
/* Upon entering this routine, im and imgptr are the same thing,
so we can use them interchangaebly */
/* ARGSUSED */
int
MPEG_next(IPTR im, int n)
{
short val;
long dev;
push_globals(im);
/* display the first frame */
MpegDisplay(cur_frame);
fl_deactivate_all_forms();
report_level = -1; /* completely turn off reporting */
bit_show_form(create_form_mpg(), FL_PLACE_HOTSPOT, 1, "MPEG_Play");
/* block until ESC is entered or done is pressed */
do
{
dev = bit_qread(&val);
}
while (!(dev == KEYBD && val == 27));
bit_hide_form(create_form_mpg());
/* signify all streams are done */
im->more = 0;
/* restore some defaults */
pop_globals(im);
im->io->display(im, 0, 1);
fl_activate_all_forms();
return 0;
}
/***********************************************************************
* Play a single frame or all of them
**********************************************************************/
static void
MpegDisplay(VidStream * vid)
{
int w, h, x, y;
long owin = winget();
w = vid->mb_width * 16;
h = vid->mb_height * 16;
x = (win_w - w * magfactor) / 2;
y = (win_h - h * magfactor) / 2;
set_current_window(win_id);
reshapeviewport();
rectzoom(g_zoomx, g_zoomy);
lrectwrite(x, y, w + x - 1, y + h - 1,
(unsigned long *) vid->current->display);
if (double_buf)
swapbuffers();
set_current_window(owin);
}
/*******************************************************************
* Play a single frame or all frames in video stream
******************************************************************/
static void
MPEG_play(IPTR im, int all)
{
short val;
long dev;
int stopit, start_frame = cur_frame_no, nf;
float rate;
if (!all)
{
if ((cur_frame = mpeg_next_frame(1)))
{
MpegDisplay(cur_frame);
cur_frame_no++;
update_frame_number();
}
return;
}
lpbk: /* loop back */
reset_time();
/*
* try to display all frames, we stop only if 1. Stop is pressed (F1KEY)
* or 2. Done is pressed (ESC) or 3. stream ends
*/
stopit = 0;
while (!stopit && (cur_frame = mpeg_next_frame(1)))
{
MpegDisplay(cur_frame);
cur_frame_no++;
update_frame_number();
fl_check_forms();
/* pause if requested */
if (current_fdelay)
msleep(current_fdelay);
stopit = (fl_qtest() &&
(((dev = bit_qread(&val)) == F1KEY && val) ||
(dev == KEYBD && val == 27)));
}
/* get the rate only if more than 10 frames */
if ((nf = cur_frame_no - start_frame) > 0)
{
rate = nf * 1000.0 / (float) time_passed();
show_play_rate(rate);
}
if (loop_back)
{
MPEG_rewind(im);
goto lpbk;
}
/* remember this frame so that a redraw will show the correct frame */
fake_frame_to_bit(im);
/* record the acutal number of frames we've got */
if (!cur_frame || cur_frame_no > actual_frames)
{
actual_frames = cur_frame_no;
fl_set_counter_bounds(fn, 1, actual_frames);
}
}
/************************************************************************
* MPEG rewind
***********************************************************************/
static void
MPEG_rewind(IPTR im)
{
show_busy("");
cur_frame_no = 1;
update_frame_number();
rewind(im->fp);
set_video_stream(im->fp);
frame_re_init();
reset_mpeg_stream();
mpeg_next_frame(0);
end_busy();
}
/*******************************************************************
* Seek to a particular frame n using the file positions saved
* last time
******************************************************************/
/* ARGSUSED */
static void
MPEG_seek(int n)
{
#if 0
MPEG_rewind(imgptr); /* rewind to the beginning */
set_mpeg_seek(1); /* turn off actually working */
cur_frame_no = 0;
/* since mpeg_play will load one frame, we get one-less */
n -= 2;
while (cur_frame_no < n && cur_frame)
{
cur_frame = mpeg_next_frame(1);
cur_frame_no++;
update_frame_number();
}
/* load this frame for possible interpolation */
set_mpeg_seek(0);
cur_frame = mpeg_next_frame(1);
cur_frame_no++;
update_frame_number();
MPEG_play(imgptr, 0);
#else
M_err("MPEGSeek", "not implemented");
update_frame_number();
#endif
}
/*********************************************************************
* Convert mpeg frame to bit image to handle redraw correctly
********************************************************************/
/* fake current bit image via matrix pointer manipulations */
static void
fake_frame_to_bit(IPTR im)
{
if (cur_frame && cur_frame->current)
{
make_mat(im->mraster, cur_frame->current->display,
im->h, im->w, im->esize);
im->raster = cur_frame->current->display;
}
}
/* Do real raster movement */
static void
frame_to_bit(IPTR im)
{
if (cur_frame && cur_frame->current && cur_frame->current->display)
memcpy(im->raster, cur_frame->current->display,
im->w * im->h * im->esize);
}
/**********************************************************************
* Save and restore some global options
*********************************************************************/
static float oldmagx, oldmagy; /* orignal zoom factors */
static int oldreport; /* original report level */
/* Save */
static void
push_globals(IPTR im)
{
oldmagx = g_zoomx;
oldmagy = g_zoomy;
g_zoomx = g_zoomy = magfactor;
oldreport = report_level;
oldpic = im->raster;
}
/* Restore */
static void
pop_globals(IPTR im)
{
g_zoomx = oldmagx;
g_zoomy = oldmagy;
report_level = oldreport;
im->raster = oldpic;
make_mat(im->mraster, im->raster, im->h, im->w, im->esize);
}
/***********************************************************************
* MPEG convert
***********************************************************************/
/**** Write current frame to disk ******/
static void
frame_to_disk(void)
{
IMG_IO *wrgb = rgb_wio[currentwrite];
/* write_image will do nasty things to im->fp, need to save it for rewind */
FILE *mpegstream = imgptr->fp;
if (!cur_frame || !cur_frame->current)
return;
fake_frame_to_bit(imgptr);
sprintf(imgptr->ofile, "frame%04d.%s", cur_frame_no,
wrgb->ext ? wrgb->ext : "");
if (write_image(wrgb, imgptr, imgptr->ofile))
M_warn("MPEGConvert", "frame %d failed", cur_frame_no);
imgptr->fp = mpegstream;
}
static int
MPEG_convert(int all)
{
int stopit;
long dev;
short val;
if (!all)
{
frame_to_disk();
return 0;
}
/* Convert all frames, starting from current frame */
do
{
frame_to_disk();
cur_frame = mpeg_next_frame(1);
if (cur_frame && cur_frame->current)
cur_frame_no++;
update_frame_number();
fl_check_forms();
stopit = (fl_qtest() &&
(((dev = bit_qread(&val)) == F1KEY && val) ||
(dev == KEYBD && val == 27)));
}
while (cur_frame && !stopit);
return 0;
}
/**********************************************************************
* GUI stuff
*
*********************************************************************/
static FL_FORM *mpg;
static FL_OBJECT *conv_grp, *convt_1, *convt_all;
/**** Temporily disable conversion request while displaying ***/
static void
disable_conversion(void)
{
fl_deactivate_object(conv_grp);
fl_set_object_lcol(convt_1, FL_INACTIVE);
fl_set_object_lcol(convt_all, FL_INACTIVE);
}
/**** re-enable conversion request *******/
static void
enable_conversion(void)
{
fl_set_object_lcol(convt_1, FL_BLACK);
fl_set_object_lcol(convt_all, FL_BLACK);
fl_activate_object(conv_grp);
}
/*** Disable seeking ***/
static FL_OBJECT *framecntl, *frmtitle;
static FL_OBJECT *frcnb[5];
static void
disable_frame_seek(void)
{
FL_OBJECT **q = frcnb;
fl_deactivate_object(framecntl);
while (*q)
{
fl_set_object_lcol(*q, FL_INACTIVE);
q++;
}
fl_set_object_lcol(frmtitle, FL_INACTIVE);
}
static void
enable_frame_seek(void)
{
FL_OBJECT **q = frcnb;
fl_set_object_lcol(frmtitle, FL_RED);
while (*q)
{
fl_set_object_lcol(*q, FL_BLACK);
q++;
}
fl_activate_object(framecntl);
}
/*** Report current frame number *************/
static void
update_frame_number(void)
{
fl_set_counter_value(fn, cur_frame_no);
}
/**** Finish up GUI stuff ********************/
/* ARGSUSED */
static void
finish_it(FL_OBJECT * ob, long q)
{
fl_qenter(KEYBD, 27);
}
/************ All frames related ******************/
/*** Stop whatever we are doing now ***/
/* ARGSUSED */
static void
stop_cb(FL_OBJECT * ob, long q)
{
loop_back = 0;
fl_qenter(F1KEY, 10);
}
/** Continously play until interruped by STOP. noloop if q is true */
/* ARGSUSED */
static void
nopause_cb(FL_OBJECT * ob, long q)
{
fl_freeze_form(mpg);
disable_conversion();
disable_frame_seek();
fl_unfreeze_form(mpg);
loop_back = !q;
MPEG_play(imgptr, 1);
fl_freeze_form(mpg);
enable_conversion();
enable_frame_seek();
fl_unfreeze_form(mpg);
}
/** Play a single frame ***/
/* ARGSUSED */
static void
step_cb(FL_OBJECT * ob, long q)
{
MPEG_play(imgptr, 0);
}
/* Play last frame again */
/* ARGSUSED */
static void
prev_cb(FL_OBJECT * ob, long q)
{
MPEG_seek(cur_frame_no - 1);
MPEG_play(imgptr, 0);
}
/** Rewind the video stream **/
/* ARGSUSED */
static void
rewind_cb(FL_OBJECT * ob, long q)
{
MPEG_rewind(imgptr);
}
struct frame_cntl
{
const char *name;
void (*cb) (FL_OBJECT *, long);
int col;
};
static struct frame_cntl frame_cb[] =
{
{"PrevFrame", prev_cb, FL_YELLOW},
{"NextFrame", step_cb, FL_GREEN},
{"Rewind", rewind_cb, FL_YELLOW},
{"Play&Loop", nopause_cb, FL_RED}
};
/************ Conversion to other formats **********/
static void
init_output_formats(FL_OBJECT * ob)
{
int i, j;
/* gather all output formats */
init_w_formats();
/* collect all RGB capable formats */
for (i = j = 0; i < totalwrite; i++)
{
/*
* don't know about mpeg spec, is gray mpeg supported ? and in that
* case, replace is_rgba with IS_CPACK
*/
if (IS_RGBA(wio[i]) || (wio[i]->type == T_FLEX))
rgb_wio[j++] = wio[i];
}
total_rgbwrite = j;
fl_clear_choice(ob);
for (i = 0; i < total_rgbwrite; i++)
fl_addto_choice(ob, rgb_wio[i]->key);
fl_set_choice(ob, currentwrite + 1);
}
/* ARGSUSED */
static void
wfmt_cb(FL_OBJECT * ob, long q)
{
currentwrite = fl_get_choice(ob) - 1;
}
/********* Must block while writing ***************/
/* ARGSUSED */
static void
convert_cb(FL_OBJECT * ob, long q)
{
if (q)
{
fl_freeze_form(mpg);
disable_conversion();
disable_frame_seek();
fl_unfreeze_form(mpg);
}
MPEG_convert(q);
if (q)
{
fl_freeze_form(mpg);
enable_conversion();
enable_frame_seek();
fl_unfreeze_form(mpg);
}
}
/************ Display parameters ***************/
/*** Maginification factors ************/
static const char *magf[] =
{
"1x1", "2x2", "3x3", "4x4", "5x5"
};
/* delay between each frame, in milli-second */
static int fdelay[] =
{
0, 10, 20, 30, 40, 50, 100, 500, 1000
};
/* ARGSUSED */
static void
mag_cb(FL_OBJECT * ob, long q)
{
static int lastf = 2;
magfactor = fl_get_choice(ob);
g_zoomx = g_zoomy = magfactor;
if (cur_frame)
{
/*
* if last magifiation factor is greater than requested, clear
* screen, else look bad in double buffer mode
*/
if (lastf > magfactor)
clear_screen(win_id, 0);
MpegDisplay(cur_frame);
if (lastf > magfactor)
clear_screen(win_id, 0);
lastf = magfactor;
}
}
/* ARGSUSED */
static void
delay_cb(FL_OBJECT * ob, long q)
{
int i = fl_get_choice(ob) - 1;
if (i >= 0)
current_fdelay = fdelay[i];
}
/* ARGSUSED */
static void
frame_seek_cb(FL_OBJECT * ob, long q)
{
int n = fl_get_counter_value(ob);
if (n == cur_frame_no) /* this happens if at end or beginning */
return;
MPEG_seek(n);
}
static FL_OBJECT *playrate;
static void
show_play_rate(float rate)
{
char srate[25];
sprintf(srate, "%.2ff/s", rate);
fl_set_object_label(playrate, srate);
}
static FL_FORM *
create_form_mpg(void)
{
FL_OBJECT *obj;
int i;
float x, y, dx, dy;
int nbt = sizeof(frame_cb) / sizeof(frame_cb[0]);
if (mpg)
return mpg;
mpg = fl_bgn_form(FL_UP_BOX, 280.0, 235.0);
obj = fl_add_button(FL_HB, 0.0, 0.0, 280.0, 235.0, "");
fl_set_call_back(obj, help_cb, HELP_MPEG);
obj = fl_add_text(FL_NT, 40.0, 200.0, 200.0, 20.0, "MPEG control");
fl_set_object_lcol(obj, 4);
fl_set_object_lsize(obj, 16.000000);
fl_set_object_align(obj, FL_ALIGN_CENTER);
/* overall controls * */
obj = fl_add_button(FL_NB, 190.0, 15.0, 75.0, 25.0, "Done");
fl_set_object_lsize(obj, 10.0);
fl_set_call_back(obj, finish_it, 0);
fl_set_object_color(obj, FL_MAGIC1, FL_YELLOW);
obj = fl_add_button(FL_NB, 190.0, 45.0, 75.0, 25.0, "Stop");
fl_set_object_lsize(obj, 10.0);
fl_set_call_back(obj, stop_cb, 0);
fl_set_object_color(obj, FL_MAGIC1, FL_YELLOW);
/* conversion group */
conv_grp = fl_bgn_group();
convt_1 = obj = fl_add_button(FL_NB, 190.0, 130.0, 75.0, 25.0, "Convert");
fl_set_object_lsize(obj, 10.0);
fl_set_object_color(obj, FL_MAGIC1, FL_YELLOW);
fl_set_call_back(obj, convert_cb, 0);
convt_all = obj = fl_add_button(FL_NB, 190.0, 95.0, 75.0, 25.0, "ConvertAll");
fl_set_object_lsize(obj, 10.0);
fl_set_object_color(obj, FL_MAGIC1, FL_RED);
fl_set_call_back(obj, convert_cb, 1);
/* all formats */
obj = fl_add_choice(FL_NORMAL_CHOICE, 190.0, 165.0, 65.0, 20.0, "");
fl_set_object_boxtype(obj, FL_ROUNDED_BOX);
fl_set_choice_fontsize(obj, 10.0);
init_output_formats(obj);
fl_set_call_back(obj, wfmt_cb, 0);
fl_end_group();
/********** Frame controls *************/
framecntl = fl_bgn_group();
obj = fl_add_box(FL_SHADOW_BOX, 10.0, 10.0, 170.0, 145.0, "");
frmtitle = obj = fl_add_text(FL_NT, 40.0, 135.0, 120.0, 15.0,
"FrameControl");
fl_set_object_lcol(obj, 1);
fl_set_object_align(obj, FL_ALIGN_CENTER);
fl_set_object_lsize(obj, 12.0);
fl_set_object_lstyle(obj, FL_BOLD_STYLE);
x = 20.0;
y = 70.0;
dx = 75.0;
dy = 25.0;
for (i = 0; i < nbt; i += 2, x = 20, y -= dy)
{
frcnb[i] = obj = fl_add_button(FL_NB, x, y, dx, dy, frame_cb[i].name);
fl_set_object_lsize(obj, 10.0);
fl_set_object_color(obj, FL_MAGIC1, frame_cb[i].col);
fl_set_call_back(obj, frame_cb[i].cb, 0);
x += dx;
if ((i + 1) < nbt)
{
frcnb[i + 1] = obj = fl_add_button(FL_NB, x, y, dx, dy,
frame_cb[i + 1].name);
fl_set_object_lsize(obj, 10.0);
fl_set_object_color(obj, FL_MAGIC1, frame_cb[i + 1].col);
fl_set_call_back(obj, frame_cb[i + 1].cb, 0);
}
}
/* a large one for play */
frcnb[i] = obj = fl_add_button(FL_NB, 20, y - 3, 2 * dx, dy + 2, "Play");
fl_set_object_lsize(obj, 10.0);
fl_set_object_color(obj, FL_MAGIC1, FL_GREEN);
fl_set_call_back(obj, nopause_cb, 1);
fl_set_form_hotspot(mpg, 20.0 + dx, (y - 3) + 0.5 * dy);
/* current frame number */
fn = obj = fl_add_counter(FL_NC, 20.0, 100.0, 150.0, 25.0, "");
fl_set_object_lsize(obj, 10.0);
/* properties */
fl_set_counter_precision(obj, 0);
fl_set_counter_bounds(obj, 1, 2000);
fl_set_counter_value(obj, 1);
fl_set_counter_step(obj, SEEKSMALL, SEEKLARGE);
fl_set_call_back(obj, frame_seek_cb, 0);
fl_end_group();
dy = 20;
dx = 55;
x = 10.0;
y = 165.0;
/***** Magification factors ********/
obj = fl_add_choice(FL_NORMAL_CHOICE, x, y, dx, dy, "");
fl_set_object_boxtype(obj, FL_ROUNDED_BOX);
fl_set_choice_fontsize(obj, 10.0);
for (i = 0; i < sizeof(magf) / sizeof(magf[0]); i++)
fl_addto_choice(obj, magf[i]);
fl_set_choice(obj, magfactor);
fl_set_call_back(obj, mag_cb, 0);
x += dx + 2;
/****** Delay factors **********/
obj = fl_add_choice(FL_NORMAL_CHOICE, x, y, dx, dy, "");
fl_set_object_boxtype(obj, FL_ROUNDED_BOX);
fl_set_object_lsize(obj, 10.0);
for (i = 0; i < sizeof(fdelay) / sizeof(fdelay[0]); i++)
fl_addto_choice(obj, itoa(fdelay[i]));
fl_set_choice(obj, 1); /* default no delay */
fl_set_call_back(obj, delay_cb, 0);
x += dx + 2;
/***** final statistics *******/
playrate = obj = fl_add_text(FL_NT, x, y, dx, dy, "? f/s");
fl_set_object_boxtype(obj, FL_BORDER_BOX);
fl_set_object_lsize(obj, 10.0);
fl_set_object_align(obj, FL_ALIGN_CENTER);
fl_end_form();
return mpg;
}
#endif